/**
 * 
 */
package it.uniroma3.engine.util;

import it.uniroma3.engine.dictionary.DistancesImplementations;
import it.uniroma3.engine.model.CandidateProteinName;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;

import org.apache.commons.io.input.ReaderInputStream;
import org.apache.commons.lang3.StringUtils;

/**
 * @author Accosta Augusto
 * @author Lopez Elisabetta
 *
 */
public class Utils {

	public static final String SPELLED_OUT_GREEK_LETTERS_ENCODED = "spelledOutGreekLettersEncoded.txt";
	public static final String SPELLED_OUT_ROMAN_NUMBERS = "spelledOutRomanNumbers.txt";
	
	public static final String SPELLED_OUT_GREEK_LETTERS_ENCODED_LOCAL = "GreekEncodingSpelledOut.properties";
	public static final String SPELLED_OUT_ROMAN_NUMBERS_LOCAL = "RomansEncodingSpelledOut.properties";
	
	public static Map<String, String> spelledOutGreekLettersEncodedMap = new HashMap<String, String>();
	public static Map<String, String> spelledOutRomanNumbers = new HashMap<String, String>();	
	
	/**
	 * This method filer the input text removing the following characters ()[]{}?!:~^
	 * 
	 * @param entryTest
	 * @return
	 */
	public static String inputFilter(String entryTest) {

		// TODO attenzione alle stringhe che contengono potenziali caratteti "\"
		String result = entryTest; //.replaceAll("-", " ");
		
		result = StringUtils.remove(result, "(");
		result = StringUtils.remove(result, ")");
		result = StringUtils.remove(result, "[");
		result = StringUtils.remove(result, "]");
		result = StringUtils.remove(result, "{");
		result = StringUtils.remove(result, "}");
		result = StringUtils.remove(result, "?");
		result = StringUtils.remove(result, "!");
		result = StringUtils.remove(result, ":");
		result = StringUtils.remove(result, "~");
		result = StringUtils.remove(result, "^");
//		result = StringUtils.remove(result, "+");
//		result = StringUtils.remove(result, "-");
		
		return result;
	}

	/**
	 * This method transform a simple protein name in a spelled out once
	 * 
	 * @param proteinName
	 * @return
	 */
	public static String spelledOut(String proteinName) {
		RomanNumbersFormatter romanNumbersFormatter = new RomanNumbersFormatter();
		NumbersFormatter numbersFormatter = new NumbersFormatter();
		GreekLetterFormatter greekLetterFormatter = new GreekLetterFormatter();
		
		String spelledOutProteinName = "";
		
		try {
		
			spelledOutProteinName = romanNumbersFormatter.substituteRomansNumbersByArabicNumbers(proteinName);		
			spelledOutProteinName = numbersFormatter.substituteNumbersBySpelledOutNumbers(spelledOutProteinName); // inputAfterSpelledOutRomans
			spelledOutProteinName = greekLetterFormatter.substituteGreekSimbolsBySpelledOutLetters(spelledOutProteinName);
		
		} catch (IOException e) {
			// TODO Auto-generated catch block
			System.err.println("Spelled out failure!");
			e.printStackTrace();
		}
		
		return removeDoubleWhiteSpace(spelledOutProteinName); // TODO eventualmente rimuovere la funzione removeDoubleWhiteSpace
	}

	/**
	 * This method return a map of correspondences <key, value> read from the specified filename
	 * 
	 * @param filename
	 * @return
	 * @throws IOException
	 * @deprecated
	 */
	public static Map<String, String> loadAllProperty(String filename) throws IOException {
		URL url = Utils.class.getResource(filename);
        String path = url.getPath();
        path = path.replaceAll("%20"," ");
        
        InputStream file = new FileInputStream(path);
        Reader fileReader = new InputStreamReader(file);
        ReaderInputStream reader = new ReaderInputStream(fileReader, Charset.forName("UTF-8"));
        Properties property = new Properties();
        property.load(reader);
        Map<String, String> propertyMap = new HashMap<String, String>();
        
        Set<Entry<Object, Object>> propertySet = property.entrySet();
        for (Entry<Object, Object> entry : propertySet) {
			propertyMap.put(entry.getKey().toString(), entry.getValue().toString());
		}
        
        return propertyMap;
	}
	
	/**
	 * This method populates the spelled out lists,
	 * to be later used by the Protein Name handlers.
	 */
	public static void spelledOutMapsPopulatorLocal() {
		try {
			spelledOutGreekLettersEncodedMap = loadAllProperty(SPELLED_OUT_GREEK_LETTERS_ENCODED_LOCAL);
			spelledOutRomanNumbers = loadAllProperty(SPELLED_OUT_ROMAN_NUMBERS_LOCAL);
		} catch (IOException e) {
			System.out.println("IOException when trying to load the spelled out lists");
			e.printStackTrace();
		}
		
	}

	/**
	 * This method remove the duplicates value in the list.
	 * First of all, order by rank the input list, at this point, remove the candidates with the lowest value rank.
	 * Second reorder the modified list by candidates names.
	 *  
	 * @param candidatesList
	 * @return
	 */
	public static List<CandidateProteinName> removeDuplicatedCandidatesAndReorder(List<CandidateProteinName> candidatesList) {
		List<CandidateProteinName> modifiedListOfcandidates = new LinkedList<CandidateProteinName>();
		Set<CandidateProteinName> noDuplicateCandidates = new HashSet<CandidateProteinName>();
		
		Collections.sort(candidatesList);
		
		for (CandidateProteinName candidateProteinName : candidatesList) {
			noDuplicateCandidates.add(candidateProteinName);
		}
		
		modifiedListOfcandidates.addAll(noDuplicateCandidates);
		
		Collections.sort(modifiedListOfcandidates);
		
		return modifiedListOfcandidates;
	}
	
	/**
	 * 
	 * @param candidateList
	 * @return
	 */
	public static List<CandidateProteinName> mergeDuplicatedCandidatesAndRetrieveTotalScore(List<CandidateProteinName> candidateList) {
		List<CandidateProteinName> updatedCandidatesList = new LinkedList<CandidateProteinName>();
		Map<String, Float> candidatesMap = new HashMap<String, Float>();
		
		for (CandidateProteinName candidateProteinName : candidateList) {
			if(candidatesMap.containsKey(candidateProteinName.getCandidateProtein())) {
				float actualCandidateRank = candidateProteinName.getRank();
				float storedCandidateRank = candidatesMap.get(candidateProteinName.getCandidateProtein());
				candidatesMap.remove(candidateProteinName.getCandidateProtein()); // rimuovo quello vecchio
				candidatesMap.put(candidateProteinName.getCandidateProtein(), actualCandidateRank+storedCandidateRank);
			}
			else {
				candidatesMap.put(candidateProteinName.getCandidateProtein(), candidateProteinName.getRank());
			}
		}
		
		updatedCandidatesList = getListOfSingleUpdatedCandidatesFromCandidatesMap(candidatesMap);
		
		return updatedCandidatesList;
	}
	
	private static List<CandidateProteinName> getListOfSingleUpdatedCandidatesFromCandidatesMap(Map<String, Float> candidatesMap) {
		List<CandidateProteinName> candidatesList = new LinkedList<CandidateProteinName>();
		
		for (Map.Entry<String, Float> entrySet : candidatesMap.entrySet()) {
			candidatesList.add(new CandidateProteinName(entrySet.getKey(), entrySet.getValue()));
		}
		
		return candidatesList;
	}

	/**
	 * This method transform the actual score in normalized score 
	 * 
	 * @param actualScore
	 * @param maxScoreAsUpperBound
	 * @return
	 */
	public static float normalizeScore(float actualScore, float maxScoreAsUpperBound) {
		return (100 * actualScore) / maxScoreAsUpperBound;
	}
	
	/**
	 * 
	 * @param string
	 * @return
	 */
	public static String removeDoubleWhiteSpace(String string) {
		return string.replaceAll("  ", " ");
	}
	
	/**
	 * 
	 * @param entryTest
	 * @param candidatesList
	 * @return
	 */
	public static List<CandidateProteinName> postProcessingWithDistances(String entryTest, List<CandidateProteinName> candidatesList) {
		List<CandidateProteinName> candidatesListWithNewRank = new LinkedList<CandidateProteinName>();
		
		for (CandidateProteinName candidate : candidatesList) {
			String candidateName = candidate.getCandidateProtein();
			double spelledOutDistance = DistancesImplementations.weightedEditDistance(Utils.spelledOut(entryTest), Utils.spelledOut(candidateName)); 
			double fullnameDistance = DistancesImplementations.weightedEditDistance(entryTest, candidateName);
			
			if((fullnameDistance == 0.0d) || (spelledOutDistance == 0.0d)) {
				candidatesListWithNewRank.add(new CandidateProteinName(candidateName, candidate.getRank()+DistancesImplementations.NO_DISTANCE));
			}
			else {
				if(((fullnameDistance >= 1.0d) && (fullnameDistance <= 5.0d)) || ((spelledOutDistance >= 1.0d) && (spelledOutDistance <= 5.0d))) {
					candidatesListWithNewRank.add(new CandidateProteinName(candidateName, candidate.getRank()+DistancesImplementations.NOT_MUCH_DISTANCE));
				}
				else {
					if((fullnameDistance > 5.0d) ||	(spelledOutDistance > 5.0d)) {
						candidatesListWithNewRank.add(new CandidateProteinName(candidateName, candidate.getRank()+DistancesImplementations.SO_FAR_DISTANCE));
					}
				}
			}
		}
		
		return candidatesListWithNewRank;
	}
	
	public static void main(String[] args) {
//		List<CandidateProteinName> listTestValues = new LinkedList<CandidateProteinName>();
//		
//		listTestValues.add(new CandidateProteinName("p twenty-one-activated kinase 1", 1.0f));
//		listTestValues.add(new CandidateProteinName("p twenty-one-activated kinase 1", 1.2f));
//		listTestValues.add(new CandidateProteinName("p 21 activated kinase 1", 1.2f));
//		listTestValues.add(new CandidateProteinName("p twenty-one-activated kinase 1", 2.6f));
//		listTestValues.add(new CandidateProteinName("abc", 1.2f));
		
//		System.out.println(Utils.mergeDuplicatedCandidatesAndRetrieveTotalScore(listTestValues));
		
		Utils.spelledOutMapsPopulatorLocal();
		
//		System.out.println(DistancesImplementations.editDistance("p21-activated kinase one", "p21-activated kinase 1"));
//		System.out.println(DistancesImplementations.editDistance(Utils.spelledOut("p21-activated kinase one"), Utils.spelledOut("p21-activated kinase 1")));

//		System.out.println(DistancesImplementations.editDistance("p twenty-one-activated kinase one", "p21-activated kinase 7"));
//		System.out.println(DistancesImplementations.editDistance(Utils.spelledOut("p twenty one activated kinase one"), Utils.spelledOut("p21-activated kinase 1")));
		
//		System.err.println(Utils.spelledOut("p twenty-one-activated kinase one"));
//		System.err.println(Utils.spelledOut("p21-activated protein II kinase I"));
		
//		double fullnameDistance = DistancesImplementations.weightedEditDistance("p twenty-one-activated kinase one", "p21-activated kinase 1"); // 99
//		double spelledOutDistance = DistancesImplementations.weightedEditDistance(Utils.spelledOut("p twenty-one-activated kinase one"), Utils.spelledOut("p21-activated kinase 1")); // 17 
		
		System.err.println(Utils.spelledOut("p21-activated kinase 1"));
//		System.err.println(Utils.spelledOut("p twenty-one-activated kinase one"));
//		System.out.println(Utils.spelledOut("Isophentenyl diphosphate Δ-isomerase"));
//		System.out.println(fullnameDistance);
//		System.out.println(spelledOutDistance);
		
	}
	
}
